package com.sxhuayuan.parking.job;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.sxhuayuan.parking.entity.ParkingLot;
import com.sxhuayuan.parking.service.ParkingLotService;
import com.sxhuayuan.parking.service.RedisService;
import com.sxhuayuan.parking.utils.SettingUtils;

/**
 * 车场网络状态监控
 * 
 * @author DELL
 *
 */
@Component("parkingLotNetworkMonitor")
public class ParkingLotNetworkMonitorJob implements InitializingBean, DisposableBean {

	private Logger log = LoggerFactory.getLogger(getClass());

	private CloseableHttpClient httpClient = null;
	private ThreadPoolExecutor exec = null;
	private Set<ParkingLotDomain> lots = null;

	public static final String PARKINGLOT_URLS_IN_REDIS = "parking_lot_urls";
	@Resource(name = "parkingLotServiceImpl")
	ParkingLotService lotService;
	@Resource(name = "redisServiceImpl")
	RedisService redisService;

	@Override
	public void afterPropertiesSet() throws Exception {
		RequestConfig config = RequestConfig.custom().setConnectTimeout(6000).build();
		httpClient = HttpClients.custom().setDefaultRequestConfig(config).setConnectionTimeToLive(60, TimeUnit.SECONDS).build();
		exec = (ThreadPoolExecutor) Executors.newCachedThreadPool();
	}

	@Override
	public void destroy() throws Exception {
		try {
			exec.shutdown();
		} catch (Exception e) {
			log.error("", e);
		}
		try {
			httpClient.close();
		} catch (Exception e) {
			log.error("", e);
		}
	}

	@Scheduled(cron = "0 0/10 0 * * ?")
	@Async
	public void parkingLotNetworkMonitorJob() {
		log.debug("任务开启，检查车场接口是否正常");
		String json = redisService.getString(PARKINGLOT_URLS_IN_REDIS);
		if (json == null) {
			List<ParkingLot> list = lotService.findAll();
			lots = new HashSet<>();
			for (ParkingLot lot : list) {
				ParkingLotDomain domain = new ParkingLotDomain();
				domain.setName(lot.getName());
				domain.setUrl(lot.getApiUrl());
				domain.setFailCount(0);
				domain.setLastNoticeTime(0L);
				lots.add(domain);
			}
			redisService.saveString(PARKINGLOT_URLS_IN_REDIS, JSON.toJSONString(lots));
			log.debug("缓存lots列表...size={}", lots.size());
		} else {
			lots = new HashSet<>(JSON.parseArray(json, ParkingLotDomain.class));
		}
		for (ParkingLotDomain e : lots) {
			exec.execute(new TestTask(e));
		}
		while (true) {
			if (exec.getActiveCount() == 0) {
				log.debug("任务已完成!");
				redisService.updateString(PARKINGLOT_URLS_IN_REDIS, JSON.toJSONString(lots));
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
			}
		}
	}

	class TestTask extends Thread {

		private ParkingLotDomain domain = null;

		public TestTask(ParkingLotDomain domain) {
			this.domain = domain;
		}

		@Override
		public void run() {
			testHttp();
		}

		private void testHttp() {
			String uuid = RandomStringUtils.randomAlphanumeric(8);
			log.debug("[{}]测试接口url是否正常[{}]", uuid, domain);
			HttpResponse resp = null;
			try {
				resp = httpClient.execute(new HttpGet(domain.getUrl()));
				if (resp == null) {
					log.debug("[{}]访问超时", uuid);
					domain.setFailCount(0);
					domain.setLastNoticeTime(0L);
				}
			} catch (ClientProtocolException e) {
				log.error("", e);
			} catch (IOException e) {
				log.debug("[{}]连接失败:{}", uuid, domain.getUrl());
				domain.setFailCount(domain.getFailCount() + 1);
				if (System.currentTimeMillis() - domain.getLastNoticeTime() >= SettingUtils.get().getSystemErrorNoticeMinIntervalSec() * 60 * 1000) {
					// notice(domain);
					log.debug("[{}]发送通知：{}", uuid, domain.getName());
					domain.setLastNoticeTime(System.currentTimeMillis());
				}
			}
			lots.add(domain);
		}
	}

	static class ParkingLotDomain {
		private String name;
		private String url;
		private Integer failCount = 0;
		private Long lastNoticeTime = 0L;

		public final String getName() {
			return name;
		}

		public final void setName(String name) {
			this.name = name;
		}

		public final String getUrl() {
			return url;
		}

		public final void setUrl(String url) {
			this.url = url;
		}

		public final Integer getFailCount() {
			return failCount;
		}

		public final void setFailCount(Integer failCount) {
			this.failCount = failCount;
		}

		public final Long getLastNoticeTime() {
			return lastNoticeTime;
		}

		public final void setLastNoticeTime(Long lastNoticeTime) {
			this.lastNoticeTime = lastNoticeTime;
		}

		@Override
		public String toString() {
			return JSON.toJSONString(this);
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == null)
				return false;
			if (!(obj instanceof ParkingLotDomain))
				return false;
			ParkingLotDomain domain = (ParkingLotDomain) obj;
			if (domain.getUrl().equals(this.url) && domain.getName().equals(this.name))
				return true;
			return false;
		}
	}
}